﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Crypto.Signers;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Crypto.Digests;

namespace TheClerk.Forms.Templates.raw.Envelope
{
    #region internal Proof
    /// <summary>
    /// Signature over AssertionCommitment
    /// </summary>
    /// <remarks>
    /// Proof ::= SEQUENCE {
    ///   -- the digest algorithm used
    ///     digestAlgorithm AlgorithmIdentifier,
    ///     keyIdentifier OCTET STRING OPTIONAL,
    ///   -- over AssertionCommitment
    ///     signature OCTET STRING }
    /// 	
    /// A Proof is the product of one and only one Chop.
    /// </remarks>
    class Proof
    {
        Stream toDigest;
        byte[] keyIdentifier = null;
        byte[] signature;
        Org.BouncyCastle.Crypto.IDsa mySigner;
        Org.BouncyCastle.Crypto.IDigest myDigest;
        Chop myChop;

        internal Proof(Org.BouncyCastle.Crypto.IDigest hash, Stream input, Chop thisChop)
        {
            toDigest = input;
            myDigest = hash;
            myChop = thisChop;
        }

        internal Stream Serialize()
        {
            System.Diagnostics.Debug.Assert(myChop != null);
            if (myChop == null)
                throw new InvalidOperationException("no Chop equipped");
            MemoryStream s = new MemoryStream();
            var o = new DerSequenceGenerator(s);
            // TODO add keyIdentifier support, needs the Chop at this level
            // signatureAlgorithm, no keyIdentifier (yet), signature
            mySigner = null;

            if (myDigest.AlgorithmName.Substring(0, 3).Equals("SHA"))
                mySigner = new Org.BouncyCastle.Crypto.Signers.ECDsaSigner();
            if (myDigest.AlgorithmName.Substring(0, 4).ToUpper().Equals("GOST"))
                mySigner = new Org.BouncyCastle.Crypto.Signers.ECGost3410Signer();
            if (myDigest.AlgorithmName.Substring(0, 4).Equals("RIPE"))
                mySigner = new Org.BouncyCastle.Crypto.Signers.ECNRSigner();
            if (mySigner == null)
                throw new InvalidOperationException("not SHA, GOST, or RIPE");

            o.AddObject(Org.BouncyCastle.Security.DigestUtilities.GetObjectIdentifier(myDigest.AlgorithmName));

            byte[] arrayToDigest = new byte[toDigest.Length];
            toDigest.Read(arrayToDigest, 0, arrayToDigest.Length);
            byte[] digestOutput = new byte[myDigest.GetDigestSize()];
            myDigest.BlockUpdate(arrayToDigest, 0, arrayToDigest.Length);
            myDigest.DoFinal(digestOutput, 0);
            mySigner.Init(true, myChop.PrivateKey);
            var i = mySigner.GenerateSignature(digestOutput);
            MemoryStream temporary = new MemoryStream();
            var myout = i[0].ToByteArray();
            temporary.Write(myout, 0, myout.Length);
            myout = i[1].ToByteArray();
            temporary.Write(myout, 0, myout.Length);
            temporary.Position = 0;
            myout = new byte[temporary.Length];
            temporary.Read(myout, 0, myout.Length);
            o.AddObject(new Org.BouncyCastle.Asn1.DerOctetString(myout));
            o.Close();
            s.Position = 0;
            // TODO: NotImplemented
            //throw new NotImplementedException();
            return s;
        }

        internal void EquipChop(Chop input)
        {
            myChop = input;
        }

        internal void SetStream(Stream value) { toDigest = value; }

        internal Asn1Encodable ToAsn1Encodable()
        {
            return Asn1Object.FromStream(Serialize());
        }

    }
    #endregion
}
